using System;
using System.Collections.Generic;
using System.ComponentModel.Design;
using System.IO;
using System.Runtime.InteropServices;
using ResourceLoader.DeserializeTool;

namespace ResourceLoader.Loader
{
    public class ResourcesLoader : IDisposable
    {
        private object _loadLocker; //加载锁
        private Dictionary<string, SinglePackage> _packages; //资源加载合集字典
        private Dictionary<string, List<MultiResourceInfo>> _resDictionary; //资源对应信息合集
        private Queue<string> _resourceFilePath; //所有资源包的队列
        private ResourceInfo _resourceInfo; //加载资源时用的临时信息(线程安全)
        private bool _initFlag; //初始化标记

        #region Instance

        static ResourcesLoader _instance = null;
        static readonly object _padlock = new object();

        private static ResourcesLoader Instance
        {
            get
            {
                if (_instance == null)
                {
                    lock (_padlock)
                    {
                        if (_instance == null)
                        {
                            _instance = new ResourcesLoader();
                        }
                    }
                }

                return _instance;
            }
        }

        #endregion

        ResourcesLoader()
        {
            _loadLocker = new object();
            _packages = new Dictionary<string, SinglePackage>();
            _resDictionary = new Dictionary<string, List<MultiResourceInfo>>();
            _resourceFilePath = new Queue<string>();
        }

        #region Static Function

        /// <summary>
        /// 加载目录下所有资源包
        /// </summary>
        /// <param name="directory">目录地址</param>
        public static void LoadPackageDirectory(string directory)
        {
            Instance.LoadPackageDirectoryNoneStatic(directory);
        }

        /// <summary>
        /// 配置资源包路径
        /// </summary>
        /// <param name="packagePath">资源包路径</param>
        public static void LoadPackagePath(string packagePath)
        {
            Instance.LoadPackagePathNoneStatic(packagePath);
        }

        /// <summary>
        /// 根据文件名加载 
        /// </summary>
        /// <param name="buffer">提供资源存储的byte数组</param>
        /// <param name="fileName">资源文件名</param>
        /// <returns></returns>
        public static long Load([In, Out] byte[] buffer, string fileName)
        {
            return Instance.LoadNoneStatic(buffer, fileName);
        }

        /// <summary>
        /// 获得文件信息
        /// </summary>
        /// <param name="fileName">资源文件名</param>
        /// <returns></returns>
        public static ResourceInfo TryGetFileInfo(string fileName)
        {
            return Instance.TryGetFileInfoNoneStatic(fileName);
        }

        /// <summary>
        /// 文件是否存在
        /// </summary>
        /// <param name="fileName">资源文件名</param>
        /// <returns></returns>
        public static bool Exist(string fileName)
        {
            return Instance.ExistNoneStatic(fileName);
        }

        #endregion

        /// <summary>
        /// 加载目录下所有资源包
        /// </summary>
        /// <param name="directory"></param>
        private void LoadPackageDirectoryNoneStatic(string directory)
        {
            var packagePaths = Directory.GetFiles(directory);
            foreach (var path in packagePaths)
            {
                if (path.EndsWith(".pk"))
                {
                    LoadPackagePathNoneStatic(path);
                }
            }
        }

        /// <summary>
        /// 配置资源包路径
        /// </summary>
        /// <param name="packagePath"></param>
        private void LoadPackagePathNoneStatic(string packagePath)
        {
            PathChecker.CheckFilesExit(packagePath);

            if (_resourceFilePath.Contains(packagePath))
            {
                throw new Exception($"路径${packagePath}已被加载, 请勿重复加载");
            }

            UpdateResDictionary(packagePath);
            _resourceFilePath.Enqueue(packagePath);
            _initFlag = true;
            Console.WriteLine($"加载资源包{packagePath}");
        }

        /// <summary>
        /// 根据文件名加载 
        /// </summary>
        /// <param name="byteArray"></param>
        /// <param name="fileName"></param>
        /// <returns></returns>
        private long LoadNoneStatic([In, Out] byte[] byteArray, string fileName)
        {
            CheckInit();
            if (!_resDictionary.TryGetValue(fileName, out var multiResourceInfo))
            {
                throw new Exception("没有找到相关资源");
            }

            var singlePackage = multiResourceInfo[0].SinglePackage; //此处目前策略为选择第一个匹配到的资源
            long readCount = -1;
            //找到对应资源包, 进行资源加载, 这里已经不需要锁
            if (singlePackage != null)
            {
                readCount = singlePackage.Load(byteArray, multiResourceInfo[0].Path);
            }

            return readCount;
        }

        private bool ExistNoneStatic(string fileName)
        {
            return _resDictionary.ContainsKey(fileName);
        }

        /// <summary>
        /// 获得文件信息
        /// </summary>
        /// <param name="fileName"></param>
        /// <returns></returns>
        private ResourceInfo TryGetFileInfoNoneStatic(string fileName)
        {
            CheckInit();
            lock (_loadLocker)
            {
                _resourceInfo.Clear();
                foreach (SinglePackage sp in _packages.Values)
                {
                    _resourceInfo = sp.TryGetFileInfo(fileName);
                    if (!_resourceInfo.IsNull())
                    {
                        return _resourceInfo;
                    }
                }
            }

            throw new Exception($"请检查, 没有资源{fileName}");
        }

        public void Dispose()
        {
            CheckInit();
            foreach (var singlePackage in _packages)
            {
                singlePackage.Value.Dispose();
            }
        }

        #region Internal Function

        /// <summary>
        /// 更新字典
        /// </summary>
        /// <param name="packagePath"></param>
        private void UpdateResDictionary(string packagePath)
        {
            SinglePackage singlePackage = GetSinglePackageByPackageName(packagePath);
            foreach (var resourceInfo in singlePackage.ResourceDictionary)
            {
                if (!_resDictionary.TryGetValue(resourceInfo.Value.FileName, out var multiResourceInfo))
                {
                    multiResourceInfo = new List<MultiResourceInfo>();
                    _resDictionary.Add(resourceInfo.Value.FileName, multiResourceInfo);
                }

                multiResourceInfo.Add(new MultiResourceInfo(singlePackage, resourceInfo.Key));
            }
        }

        /// <summary>
        /// 通过文件名获得字典key
        /// </summary>
        /// <param name="fileName"></param>
        /// <returns></returns>
        private string GetDictionaryByFileName(string fileName)
        {
            foreach (var sp in _packages)
            {
                var resInfo = sp.Value.TryGetFileInfo(fileName);
                if (!resInfo.IsNull())
                    return sp.Key;
            }

            return null;
        }


        /// <summary>
        /// 通过目录名字获得资源加载实例
        /// </summary>
        /// <param name="packagePath"></param>
        /// <returns></returns>
        private SinglePackage GetSinglePackageByPackageName(string packagePath)
        {
            SinglePackage singlePackage;
            if (!_packages.TryGetValue(packagePath, out singlePackage))
            {
                singlePackage = new SinglePackage(packagePath);
                _packages.Add(packagePath, singlePackage);
            }

            return singlePackage;
        }

        private void CheckInit()
        {
            if (!_initFlag)
                throw new Exception("资源加载器尚未初始化.");
        }

        /// <summary>
        /// 根据文件名获得资源加载实例
        /// </summary>
        /// <param name="fileName"></param>
        /// <returns></returns>
        private SinglePackage GetSinglePack(string fileName)
        {
            //屏蔽包名, 只需要提供fileName
            lock (_loadLocker)
            {
                var dir = GetDictionaryByFileName(fileName);
                if (dir != null)
                    return _packages[dir];

                while (_resourceFilePath.Count > 0)
                {
                    dir = _resourceFilePath.Dequeue();
                    var sp = GetSinglePackageByPackageName(dir);
                    if (!TryGetFileInfoNoneStatic(fileName).IsNull())
                        return sp;
                }
            }

            return null;
        }

        /// <summary>
        /// 通过加载信息进行加载
        /// </summary>
        /// <param name="buffer">缓存数组</param>
        /// <param name="singlePackage">单个资源包实例</param>
        /// <param name="fileName">文件名</param>
        /// <param name="resourceInfo">加载资源名</param>
        /// <returns></returns>
        private long Load([In, Out] byte[] buffer, SinglePackage singlePackage, string fileName,
            ResourceInfo resourceInfo)
        {
            if (!singlePackage.TryGetFileInfo(fileName).IsNull())
            {
                return singlePackage.Load(buffer, resourceInfo);
            }

            return -1;
        }

        #endregion
    }
}